import re
from django.db import models

"""
This comes from djangosnippets, can't remember the snippet number
"""

class SlugNotCorrectlyPrePopulated(Exception): 
    pass 

def _string_to_slug(s): 
    raw_data = s
    # normalze string as proposed on http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/251871
    # by Aaron Bentley, 2006/01/02
    try:
        import unicodedata      
        if type(raw_data) == type(u''):
            raw_data = unicodedata.normalize('NFKD', raw_data).encode('ascii', 'ignore')
        else:
            raw_data = unicodedata.normalize('NFKD', raw_data.decode('utf-8', 'replace')).encode('ascii', 'ignore')
    except:
        pass
    return re.sub(r'[^a-z0-9-]+', '_', raw_data.lower()).strip('_')

    
# as proposed by Archatas (http://www.djangosnippets.org/users/Archatas/)
def _get_unique_value(model, proposal, field_name="slug", instance_pk=None, separator="-"):
    """ Returns unique string by the proposed one.
    Optionally takes:
    * field name which can  be 'slug', 'username', 'invoice_number', etc.
    * the primary key of the instance to which the string will be assigned.
    * separator which can be '-', '_', ' ', '', etc.
    By default, for proposal 'example' returns strings from the sequence:
        'example', 'example-2', 'example-3', 'example-4', ...
    """
    if instance_pk:
        similar_ones = model.objects.filter(**{field_name + "__startswith": proposal}).exclude(pk=instance_pk).values(field_name)
    else:
        similar_ones = model.objects.filter(**{field_name + "__startswith": proposal}).values(field_name)
    similar_ones = [elem[field_name] for elem in similar_ones]
    if proposal not in similar_ones:
        return proposal
    else:
        numbers = []
        for value in similar_ones:
            match = re.match(r'^%s%s(\d+)$' % (proposal, separator), value)
            if match:
                numbers.append(int(match.group(1)))
        if len(numbers)==0:
            return "%s%s2" % (proposal, separator)
        else:
            largest = sorted(numbers)[-1]
            return "%s%s%d" % (proposal, separator, largest + 1)

def _get_fields_and_data(model):
    """
    returns list of [ [ models.SlugField object, slugFieldValue ] , [ x, y] ... ]
    """
    opts = model._meta
    slug_fields = []
    for f in opts.fields:
        if isinstance(f, models.SlugField):
            if not f.prepopulate_from:
                raise SlugNotCorrectlyPrePopulated , "Slug for %s is not prepopulated" % f.name
            prepop = []
            for n in f.prepopulate_from:
                if not hasattr(model, n):
                    raise SlugNotCorrectlyPrePopulated , "Slug for %s is to be prepopulated from %s, yet %s.%s does not exist" % (f.name , n , type(model), n)
                else:
                    prepop.append(getattr(model, n))
            slug_fields.append([f , "_".join(prepop)])
    return slug_fields
    
def slugify(sender, **kwargs):  
    for slugs in _get_fields_and_data(instance):    
        original_slug = _string_to_slug(slugs[1])
        slug = original_slug
        ct = 0;
        try:
            # See if object is new
            # To prevent altering urls, don't update slug on existing objects
            sender.objects.get(pk=instance._get_pk_val())
        except:
            slug = _get_unique_value(instance.__class__, slug, slugs[0].name, separator="_")
            setattr(instance, slugs[0].name, slug)



def _getSlugFieldVal(instance, slugField = None):
    """
    returns SlugField value for current instance
    it looks up models.SlugField object in instance fields
    and reutrns its value

    """
    fields = instance._meta
    retVal = []

    if slugField:
        retVal.append([slugField.name, getattr(instance, slugField.name)])
    else:
        for field in fields.fields:
            if isinstance(field, models.SlugField):
                retVal.append([field.name, getattr(instance, field.name)])

    return retVal



def slugifyDynamicly(sender, instance, signal, *args, **kwargs):
    """
    for certain type of records we want to let slug field to be updated
    to reflect the current record value. In example for records (CrewMember,
    Skipper), slugfield is name only we want also to update url. so if someone
    makes typo such as jack lemmon (slug field jack_lemmon), and then fixes
    this to jake lemmon (slug field jake_lemmon), slug field gets updated.

    otherwise it behaves in the same way as the slugify.
    """
    for slugs in _get_fields_and_data(instance):
        passedSlug = _string_to_slug(slugs[1])
        slug = passedSlug
        ct = 0;
        try:
            #look up object, if there is no such object,
            #exception is raised
            lookedUpInstance = sender.objects.get(pk=instance._get_pk_val())

            #find matching slug field
            slugMatch = False
            for originalSlug in _get_fields_and_data(lookedUpInstance):
                #slug field names and 'computed' values must match
                if _string_to_slug(originalSlug[1]) == slug and originalSlug[0] == slugs[0]:
                    slugMatch = True
                    break

            if slugMatch:
                print "slugMatch"
                slugList = _getSlugFieldVal(lookedUpInstance, originalSlug[0])
                slug = slugList[0][1]
            else:
                print "No Slug Match"
                slug = _get_unique_value(instance.__class__, slug, slugs[0].name, separator = "_")

            print slug
            print slugs[0].name
            setattr(instance, slugs[0].name, slug)

        except:
            slug = _get_unique_value(instance.__class__, slug, slugs[0].name, separator="_")
            setattr(instance, slugs[0].name, slug)

# ===========================
# To attach it to your model:
# ===========================
#
# dispatcher.connect(_package_.slugify, signal=signals.pre_save, sender=_your_model_)
